package com.sghd.fight.module.game.service;

import com.sghd.common.scheduler.impl.FixTimeDelayQueue;
import com.sghd.fight.module.game.service.room.IRoom;
import com.sghd.fight.module.game.service.work.RoomRunner;
import com.sghd.fight.module.game.service.work.RoomThread;
import com.sghd.fight.module.game.service.work.RoomWork;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Date;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Service
public class ThreadService {
    private static final Logger logger = LoggerFactory.getLogger(ThreadService.class);

    @Value("${room.thread.core:4}")
    private int threadCore = 4;
    /** 战斗超时队列  */
    private FixTimeDelayQueue<RoomWork> overtimeQueue;

    private RoomThread[] threads;
    private RoomThread match;
    ReadWriteLock lokc = new ReentrantReadWriteLock();

    @PostConstruct
    void init() {
        threads = new RoomThread[threadCore];
        for (int i = 0; i < threadCore; i++) {
            threads[i] = new RoomThread(i);
            threads[i].start();
        }
        match = new RoomThread("match_thread");
        match.start();
        overtimeQueue = new FixTimeDelayQueue<>(5000);
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.interrupted()) {
                    try {
                        lokc.readLock().lock();
                        final RoomWork work = overtimeQueue.take();
                        int type = work.getType();
                        switch (type) {
                            case RoomWork.ROOM: {
                                IRoom room = work.getRoom();
                                if (room == null) {
                                    logger.debug("房间[{}]未找到,Runner类型[{}]", room, work.getContent().getClass());
                                    continue;
                                }
                                submit(work);
                                break;
                            }
                            case RoomWork.MATCH:
                                submitMatch(work);
                                break;
                            default:
                                work.getContent().run(work.getEnd());
                                break;
                        }
                    } catch (Exception e) {
                        logger.error("战斗定时处理线程发生异常...", e);
                    } finally {
                        lokc.readLock().unlock();
                    }
                }
            }
        }, "战斗处理线程");
        thread.setDaemon(true);
        thread.start();
    }

    @PreDestroy
    void destory() {
        for (RoomThread thread : threads) {
            thread.stopWork();
        }
    }

    public void submit(RoomWork work) {
        long roomId = work.getRoom().getId();
        int index = (int) (roomId % threads.length);
        threads[index].addWork(work);
    }

    public void submit(IRoom room, RoomRunner runner) {
        RoomWork work = new RoomWork(room, runner, new Date());
        submit(work);
    }

    public boolean add(RoomWork roomWork) {
        return overtimeQueue.add(roomWork);
    }

    /**
     * @param work
     */
    public void remove(RoomWork work) {
        try {
            lokc.writeLock().lock();
            boolean flag = overtimeQueue.remove(work);
            if (!flag) {
                long roomId = work.getRoom().getId();
                int index = (int) (roomId % threads.length);
                RoomThread thread = threads[index];
                thread.remove(work);
            }
        } finally {
            lokc.writeLock().unlock();
        }
    }

    public void removeForMatch(RoomWork work) {
        try {
            lokc.writeLock().lock();
            boolean flag = overtimeQueue.remove(work);
            if (!flag) {
                match.remove(work);
            }
        } finally {
            lokc.writeLock().unlock();
        }
    }

    /**匹配线程*/
    public void submitMatch(RoomWork work) {
        match.addWork(work);
    }

    public void submitMatch(RoomRunner runner) {
        RoomWork work = new RoomWork(null, runner, new Date());
        submitMatch(work);
    }
}